package org.moonlight.jvm.rtda.exclusive;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.moonlight.jvm.rtda.share.Object;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

/**
 * 运行时数据区 - 线程独占区 - 线程 - 虚拟机栈 - 栈帧 - 操作数栈
 *
 * 操作数栈和局部变量表差不多，只不过操作数栈是一个栈
 *
 * @author Moonlight
 * @createTime 2023/8/16 10:49
 **/
@EqualsAndHashCode
public class OperandStack {

    /** 栈顶元素所在下标 **/
    private int topEleIdx;
    /** 使用 数组 来模拟栈，这样就不用不停的 new Slot 对象了，如果使用 Stack 或者 Deque 之类的，需要反复 new Slot 很烦 **/
    @Getter
    private Slot[] stack;

    public OperandStack(int maxStack) {
        if (maxStack > 0) {
            this.stack = new Slot[maxStack];
            for (int i = 0; i < maxStack; i++) {
                this.stack[i] = new Slot();
            }
        }
    }

    public void pushInt(int val) {
        this.stack[this.topEleIdx].setNum(val);
        this.topEleIdx++;
    }

    public int popInt() {
        this.topEleIdx--;
        return this.stack[this.topEleIdx].getNum().intValue();
    }

    public void pushFloat(float val) {
        this.stack[this.topEleIdx].setNum(val);
        this.topEleIdx++;
    }

    public float popFloat() {
        this.topEleIdx--;
        return this.stack[this.topEleIdx].getNum().floatValue();
    }

    /**
     * 事实上 long 和 double 都应该是占用两个位置的，但是这里使用了 number，所以就不进行特殊处理了
     * 否则，需要将 long 或 double 拆分成 高32位 和 低32位 分为两个 int 进行存储. 示例如下:
     *   this.stack[this.topEleIdx].setNum((int) val);
     *   this.stack[this.topEleIdx + 1].setNum((int) (val >> 32));
     *   this.topEleIdx += 2;
     *
     * @param val long 型数字
     * @createTime 11:16 2023/8/16
     * @author moonlight
     **/
    public void pushLong(long val) {
        // 事实上 long 和 double 都应该是占用两个位置的，但是这里使用了 number，所以就不进行特殊处理了
        // 否则，需要将 long 或 double 拆分成 高32位 和 低32位 分为两个 int 进行存储. 示例如下:
        // this.stack[this.topEleIdx].setNum((int) val);
        // this.stack[this.topEleIdx + 1].setNum((int) (val >> 32));
        // this.topEleIdx += 2;
        this.stack[this.topEleIdx].setNum(val);
        this.topEleIdx++;
    }

    /**
     * 事实上 long 和 double 都应该是占用两个位置的，但是这里使用了 number，所以就不进行特殊处理了
     *
     * 存入的时候将 long 或 double 拆分成 高32位 和 低32位 分为两个 int 进行存储
     * 那么取出时，又需要将其合并。示例如下:
     *   this.topEleIdx -= 2;
     *   int low = this.stack[this.topEleIdx].getNum();
     *   int high = this.stack[this.topEleIdx + 1].getNum();
     *   return (long) (high) << 32 | (long) (low);
     *
     * @createTime 11:16 2023/8/16
     * @author moonlight
     **/
    public long popLong() {
        // 事实上 long 和 double 都应该是占用两个位置的，但是这里使用了 number，所以就不进行特殊处理了
        // 存入的时候将 long 或 double 拆分成 高32位 和 低32位 分为两个 int 进行存储
        // 那么取出时，又需要将其合并。示例如下:
        // this.topEleIdx -= 2;
        // int low = this.stack[this.topEleIdx].getNum();
        // int high = this.stack[this.topEleIdx + 1].getNum();
        // return (long) (high) << 32 | (long) (low);
        this.topEleIdx--;
        return this.stack[this.topEleIdx].getNum().longValue();
    }

    public void pushDouble(double val) {
        // 事实上 long 和 double 都应该是占用两个位置的，但是这里使用了 number，所以就不进行特殊处理了
        // 否则，需要将 long 或 double 拆分成 高32位 和 低32位 分为两个 int 进行存储. 示例如下:
        // this.stack[this.topEleIdx].setNum((int) val);
        // this.stack[this.topEleIdx + 1].setNum((int) (val >> 32));
        // this.topEleIdx += 2;
        this.stack[this.topEleIdx].setNum(val);
        this.topEleIdx++;
    }

    public double popDouble() {
        // 事实上 long 和 double 都应该是占用两个位置的，但是这里使用了 number，所以就不进行特殊处理了
        // 存入的时候将 long 或 double 拆分成 高32位 和 低32位 分为两个 int 进行存储
        // 那么取出时，又需要将其合并。示例如下:
        // this.topEleIdx -= 2;
        // int low = this.stack[this.topEleIdx].getNum();
        // int high = this.stack[this.topEleIdx + 1].getNum();
        // return (long) (high) << 32 | (long) (low);
        this.topEleIdx--;
        return this.stack[this.topEleIdx].getNum().doubleValue();
    }

    public void pushRef(Object ref) {
        this.stack[this.topEleIdx].setRef(ref);
        this.topEleIdx++;
    }

    public Object popRef() {
        this.topEleIdx--;
        Object ref = this.stack[this.topEleIdx].getRef();
        // 取出之后要重置 ref 有助于垃圾回收，不然这里一直持有引用，可能内存泄露
        this.stack[this.topEleIdx].setRef(null);
        return ref;
    }

    public void pushSlot(Slot slot) {
        this.stack[this.topEleIdx] = new Slot(slot);
        this.topEleIdx++;
    }

    public Slot popSlot() {
        this.topEleIdx--;
        return this.stack[this.topEleIdx];
    }

    public void pushBoolean(boolean b) {
        if (b) {
            this.pushInt(1);
        } else {
            this.pushInt(0);
        }
    }

    public boolean popBoolean() {
        return this.popInt() == 1;
    }

    /**
     * 返回距离操作数栈顶 n 个单元格的引用变量。比如GetRefFromTop(0)返回操作数栈顶引用，GetRefFromTop(1)返回从栈顶开始的倒数第二个引用
     * @param n 距离操作数栈顶的距离
     * @return Object
     * @createTime 15:05 2023/9/12
     * @author moonlight
     **/
    public Object GetRefFromTop(int n) {
        return this.stack[this.topEleIdx - 1 - n].getRef();
    }

    public void clear() {
        this.topEleIdx = 0;
        for (Slot slot : this.stack) {
            slot.setRef(null);
        }
    }

    public static void main(String[] args) throws ParseException {
//        OperandStack s = new OperandStack(10);
//
//        s.pushInt(12);
//        s.pushDouble(3.1415d);
//        s.pushFloat(7.7f);
//        s.pushLong(111111L);
//        s.pushRef(null);
//
//        System.out.println(s.popRef());
//        System.out.println(s.popLong());
//        System.out.println(s.popFloat());
//        System.out.println(s.popDouble());
//        System.out.println(s.popInt());

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date start = sdf.parse("2023-09-13 12:00:00");
        Date end = sdf.parse("2023-09-14 15:00:00");

        // 系统时区
        ZoneId zoneId = ZoneId.systemDefault();
        // 开始时间
        LocalDateTime startLDT = start.toInstant().atZone(zoneId).toLocalDateTime();
        // 结束时间
        LocalDateTime endLDT = end.toInstant().atZone(zoneId).toLocalDateTime();
        // 间隔了多少时间
        Duration duration = Duration.between(startLDT, endLDT);

        // 相差多少小时
        System.out.println("LocalDateTime - 相差多少小时: " + duration.toHours());
        // 相差多少天
        System.out.println("LocalDateTime - 相差多少天: " + duration.toDays());

        // 一小时的毫秒数
        long hourMillis = 1000 * 60 * 60;
        // 一天的毫秒数
        long dayMillis = hourMillis * 24;
        // 两个时间相差的毫秒数
        long diff = end.getTime() - start.getTime();
        // 相差多少小时
        System.out.println("Millis - 相差多少小时: " + diff / hourMillis);
        // 相差多少天
        System.out.println("Millis - 相差多少天: " + diff / dayMillis);
        // 相差多少天又多少小时
        System.out.println("Millis - 相差多少天又多少小时: " + (diff / dayMillis) + "天又" + diff % dayMillis / hourMillis + "小时");
    }
}
